/*
* 1.无向图 双连通分量
    (1) 边的双连通分量  e-dcc  极大的不包含桥的连通块
    (2) 点的双连通分量  v-dcc  极大的不包含割点的连通块

* 2. 什么是桥, 如何找桥？ 
    桥: 删掉该边, 连通分量数量改变
    割点: 删掉该点以及附属边, 连通分量数量改变
    x和y之间是桥 <-> dfn[x] < low[y] y无论如何往上走不到x 
                    +y能到的最高的点low[y] = dfn[y]

    每个割点至少属于两个连通分量
    树里的每条边都是桥

* 3.如何找所有边的双连通分量？
        将所有桥删掉
        dfn[x] == low[x] 
        <-> x无论如何走不到x的上面
        <-> 从x开始的子树可以用一个栈存

* 本题: 
    (1)统计连通块个数 cnt
    (2)枚举从哪个连通块中删，删哪个割点，删除割点之后的连通块个数是多少？ 用 ans 记录一个全局最大值
    (3)答案就是 ans + cnt - 1
*/

#pragma GCC optimize("O1,O2,O3,Ofast")
#pragma GCC optimize("no-stack-protector,unroll-loops,fast-math,inline")
#pragma GCC target("avx,avx2,fma")
#pragma GCC target("sse,sse2,sse3,sse4,sse4.1,sse4.2,ssse3")

#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
// #define ONLINE_GUDGE
using namespace std;
using LL = long long;
const int N = 10010, M = 30010, INF = 0x3f3f3f3f;

int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp; // dfs序 最小访问时间 时间戳
// int stk[N], top; // 栈 栈顶指针
stack<int> stk; // 内置DS优化
// bool in_stk[N]; // 判断结点是否在栈中
int root, ans;

void AddEdge(int a, int b) // , int c)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++; // w[idx] = c,  
}

void Tarjan(int u){ 
    dfn[u] = low[u] = ++timestamp;
    // stk[ ++ top ] = u, in_stk[u] = true;
     
    stk.push(u); 
    // in_stk[u] = true;
    int cnt = 0;
    for(int i = h[u]; ~i; i = ne[i]){
        int v = e[i];
        if(!dfn[v]){

            // cerr << "u->v: " << " u: " << u << " v: " << v << endl;
            Tarjan(v);
            low[u] = min(low[u], low[v]); // 用u的邻点j更新u所能到达的结点最小时间戳
            if(dfn[u] <= low[v]) // 表示v节点永远都走不到u节点(u,v这两个点只能从u走到v)，所以边u-v(w[i])是一条桥边
                cnt++;
        }
        else low[u] = min(low[u], dfn[v]);
    }

    if(u != root && cnt) cnt++;

    ans = max(ans, cnt);
}

int main()
{

    #ifdef ONLINE_JUDGE

    #else
    freopen("./in.txt","r",stdin);
    #endif
    ios::sync_with_stdio(false);   
	cin.tie(0);
    
    while(cin >> n >> m, n || m)
    {
        memset(h, -1, sizeof h);
        memset(dfn, 0, sizeof dfn);
        for (int i = 1; i <= m; i ++ )
        {
            int a, b; cin >> a >> b;
            AddEdge(a, b), AddEdge(b, a);
        }

        int cnt = 0; ans = 0;
        for(root = 0; root < n; root++)
            if(!dfn[root]){
                cnt++;
                Tarjan(root);
            } 
        
        cout << ans + cnt - 1 << endl;
    }

    return 0;
}
